package com.mcy.threed.jpct.movement;

import android.util.Log;
import android.view.MotionEvent;

public class MovementHandler {
	/**
	 * Logs a lot of stuff
	 */
	public static final boolean DEBUG_MOVEMENT = false;

	/**
	 * Min 0.25 degrees
	 */
	public static final float MIN_ANGLE_XMOVEMENT = (float) (Math.PI / 720);

	/**
	 * Max 15 degrees
	 */
	public static final float MAX_ANGLE_XMOVEMENT = (float) MIN_ANGLE_XMOVEMENT * 60;

	/**
	 * Pixels->Angle ratio on the X-axis
	 * X轴上像素->角度比例因子
	 * X角度因子
	 */
	public static final float XANGLE_FACTOR = 360;

	/**
	 * Min 0.25 degrees
	 */
	public static final float MIN_ANGLE_YMOVEMENT = (float) (Math.PI / 720);

	/**
	 * Max 15 degrees
	 */
	public static final float MAX_ANGLE_YMOVEMENT = (float) MIN_ANGLE_YMOVEMENT * 60;

	/**
	 * Pixels->Angle ratio on the Y-axis
	 * Y轴上像素->角度比例因子
	 * Y角度因子
	 */
	public static final float YANGLE_FACTOR = 360;

	/**
	 * Pixels->Distance ratio
	 * 像素->距离比例因子
	 */
	public static final float DISTANCE_FACTOR = 10;

	/**
	 * Pixels->Movement ratio
	 * 像素->X移动因子
	 */
	public static final float MOVEMENT_X_FACTOR = 10;

	/**
	 * Pixels->Movement ratio
	 * 像素->Y移动因子
	 */
	public static final float MOVEMENT_Y_FACTOR = 10;

	/**
	 * 最小位移单位，和最大位移单位
	 */
	public static final float MIN_MOVEMENT_DELTA = 0.1f;
	public static final float MAX_MOVEMENT_DELTA = 5;

	/**
	 * 第一根手指ID、初始位置和最终位置
	 */
	private int _firstPointerId = MotionEvent.INVALID_POINTER_ID;
	private float startMovementx1;
	private float startMovementy1;
	private float lastMovementx1;
	private float lastMovementy1;
	/**
	 * 第二根手指ID、初始位置和最终位置
	 */
	private int _secondPointerId = MotionEvent.INVALID_POINTER_ID;
	private float startMovementx2;
	private float startMovementy2;
	private float lastMovementy2;
	private float lastMovementx2;
    /**
     * 两根手指初始距离和最终距离
     */
	private float startDistance;
	private float lastDistance;
    /**
     * 初始角度和最终角度
     */
	private float startAngle;
	private float lastAngle;
    /**
     * 获得转化后的动作
     * @return
     */
	public synchronized Movement getMovement() {
		Movement movement = new Movement();

		if (_secondPointerId != MotionEvent.INVALID_POINTER_ID) {
			float movementX1 = lastMovementx1 - startMovementx1;
			float movementY1 = lastMovementy1 - startMovementy1;
			float movementX2 = lastMovementx2 - startMovementx2;
			float movementY2 = lastMovementy2 - startMovementy2;

			if ((movementX1 > 0 && movementX2 > 0)
					|| (movementX1 < 0 && movementX2 < 0)) {
				float movementX = Math.min(Math.abs(movementX1),
						Math.abs(movementX2))
						/ MOVEMENT_X_FACTOR;
				if (movementX > MIN_MOVEMENT_DELTA
						&& movementX < MAX_MOVEMENT_DELTA) {
					if (movementX1 > 0) {
						movement.cameraMovementX = movementX;
					} else {
						movement.cameraMovementX = -movementX;
					}
				} else {
					if (DEBUG_MOVEMENT) {
						Log.d("MovementHandler",
								"getMovement(): Invalid movement: movementX=<"
										+ movementX + ">, movementX1=<"
										+ movementX1 + ">, movementX2=<"
										+ movementX2 + ">");
					}
				}
			}

			if ((movementY1 > 0 && movementY2 > 0)
					|| (movementY1 < 0 && movementY2 < 0)) {
				float movementY = Math.min(Math.abs(movementY1),
						Math.abs(movementY2))
						/ MOVEMENT_Y_FACTOR;
				if (movementY > MIN_MOVEMENT_DELTA
						&& movementY < MAX_MOVEMENT_DELTA) {
					if (movementY1 > 0) {
						movement.cameraMovementY = movementY;
					} else {
						movement.cameraMovementY = -movementY;
					}
				} else {
					if (DEBUG_MOVEMENT) {
						Log.d("MovementHandler",
								"getMovement(): Invalid movement: movementY<"
										+ movementY + ">, movementY1=<"
										+ movementY1 + ">, movementY2=<"
										+ movementY2 + ">");
					}
				}
			}
			float distanceDelta = lastDistance - startDistance;
			float distance = distanceDelta / DISTANCE_FACTOR;
			float absoluteDistance = Math.abs(distance);
			if (absoluteDistance > MIN_MOVEMENT_DELTA
					&& absoluteDistance < MAX_MOVEMENT_DELTA) {
				movement.cameraMovementZ = distance;
			} else {
				if (DEBUG_MOVEMENT) {
					Log.d("MovementHandler",
							"getMovement(): Invalid movement: distanceDelta=<"
									+ distanceDelta + ">, distance=<"
									+ distance + ">");
				}
			}

			float angleDelta = lastAngle - startAngle;
			float absoluteAngle = Math.abs(angleDelta);
			if (absoluteAngle > MIN_ANGLE_XMOVEMENT
					&& absoluteAngle < MAX_ANGLE_XMOVEMENT) {
				movement.cameraRotationY = angleDelta;
			} else {
				if (DEBUG_MOVEMENT) {
					Log.d("MovementHandler",
							"getMovement(): Invalid movement: absoluteAngle=<"
									+ absoluteAngle + ">, startAngle=<"
									+ Math.toDegrees(startAngle)
									+ ">, lastAngle=<"
									+ Math.toDegrees(lastAngle) + ">");
				}
			}
		} else if (_firstPointerId != MotionEvent.INVALID_POINTER_ID) {
			float movementX1 = lastMovementx1 - startMovementx1;
			float angle = (float) ((movementX1) / XANGLE_FACTOR);
			float absoluteAngle = Math.abs(angle);
			if (absoluteAngle > MIN_ANGLE_XMOVEMENT
					&& absoluteAngle < MAX_ANGLE_XMOVEMENT) {
				movement.cameraRotationY = angle;
			} else {
				if (DEBUG_MOVEMENT) {
					Log.d("MovementHandler",
							"getMovement(): Invalid movement: movementX=<"
									+ movementX1 + ">, angle=<" + absoluteAngle
									+ ">");
				}
			}

			float movementY1 = lastMovementy1 - startMovementy1;
			angle = (float) ((movementY1) / YANGLE_FACTOR);
			absoluteAngle = Math.abs(angle);
			if (absoluteAngle > MIN_ANGLE_YMOVEMENT
					&& absoluteAngle < MAX_ANGLE_YMOVEMENT) {
				movement.worldRotationX = angle;
			} else {
				if (DEBUG_MOVEMENT) {
					Log.d("MovementHandler",
							"getMovement(): Invalid movement: movementY=<"
									+ movementY1 + ">, angle=<" + absoluteAngle
									+ ">");
				}
			}
		}
		startMovementx1 = lastMovementx1;
		startMovementy1 = lastMovementy1;
		startMovementx2 = lastMovementx2;
		startMovementy2 = lastMovementy2;
		startDistance = lastDistance;
		startAngle = lastAngle;
		return movement;
	}
    /**
     * 处理转化操作
     * @param event
     */
	public synchronized void handleMotionEvent(MotionEvent event) {
		final int pointerIndex = event.getActionIndex();
		final int pointerId = event.getPointerId(pointerIndex);

		if (DEBUG_MOVEMENT) {
			Log.d("MovementHandler", "handleMotionEvent(pointerIndex=<"
					+ pointerIndex + ">, pointerId=<" + pointerId
					+ ">, event=<" + event.getActionMasked()
					+ ">, pointerCount=<" + event.getPointerCount() + ">");
		}
		switch (event.getActionMasked()) {
		case MotionEvent.ACTION_DOWN: {
			_firstPointerId = pointerId;
			_secondPointerId = MotionEvent.INVALID_POINTER_ID;
			startMovementx1 = event.getX(_firstPointerId);
			startMovementy1 = event.getY(_firstPointerId);
			lastMovementx1 = startMovementx1;
			lastMovementy1 = startMovementy1;
			if (DEBUG_MOVEMENT) {
				Log.d("MovementHandler", "ACTION_DOWN(firstPointerId=<"
						+ _firstPointerId + ">(" + startMovementx1 + ","
						+ startMovementy1 + ")");
			}
			break;
		}
		case MotionEvent.ACTION_UP: {
			_firstPointerId = MotionEvent.INVALID_POINTER_ID;
			_secondPointerId = MotionEvent.INVALID_POINTER_ID;
			if (DEBUG_MOVEMENT) {
				Log.d("MovementHandler", "ACTION_UP(firstPointerId=<"
						+ _firstPointerId + ">)");
			}
			break;
		}
		case MotionEvent.ACTION_POINTER_DOWN:
			if (event.getPointerCount() == 2) {
				_secondPointerId = pointerId;
				startMovementx2 = event.getX(_secondPointerId);
				startMovementy2 = event.getY(_secondPointerId);
				lastMovementx2 = startMovementx2;
				lastMovementy2 = startMovementy2;
				startDistance = calcDistance();
				startAngle = calcAngle();
				if (DEBUG_MOVEMENT) {
					Log.d("MovementHandler",
							"ACTION_POINTER_DOWN(secondPointerId=<"
									+ _secondPointerId + ">)");
				}
			}
			break;
		case MotionEvent.ACTION_POINTER_UP:
			if (pointerId == _firstPointerId) {
				// This was our active pointer going up. Choose a new
				// active pointer and adjust accordingly.
				final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
				_firstPointerId = event.getPointerId(newPointerIndex);
				int oldPointerId = _firstPointerId;
				if (DEBUG_MOVEMENT) {
					Log.d("MovementHandler",
							"ACTION_POINTER_UP(_firstPointerId=<"
									+ _firstPointerId + ">, oldPointerId=<"
									+ oldPointerId + ">)");
				}
				startMovementx1 = event.getX(newPointerIndex);
				startMovementy1 = event.getY(newPointerIndex);
			} else if (pointerId == _secondPointerId) {
				if (event.getPointerCount() > 2) {
					// This was our active secondpointer going up. Choose a new
					// active pointer and adjust accordingly.
					final int newPointerIndex = pointerIndex == 1 ? 2 : 1;
					_secondPointerId = event.getPointerId(newPointerIndex);
					int oldPointerId = _secondPointerId;
					if (DEBUG_MOVEMENT) {
						Log.d("MovementHandler",
								"ACTION_POINTER_UP(secondPointerId=<"
										+ _secondPointerId
										+ ">, oldPointerId=<" + oldPointerId
										+ ">)");
					}
					startMovementx2 = event.getX(newPointerIndex);
					startMovementy2 = event.getY(newPointerIndex);
				} else {
					_secondPointerId = MotionEvent.INVALID_POINTER_ID;
					if (DEBUG_MOVEMENT) {
						Log.d("MovementHandler",
								"ACTION_POINTER_UP(secondPointerId=<"
										+ _secondPointerId + ">)");
					}
				}
			}
			break;
		case MotionEvent.ACTION_MOVE: {
			int firstPointerIndex = event.findPointerIndex(_firstPointerId);
			int secondPointerIndex = event.findPointerIndex(_secondPointerId);
			if (event.getPointerCount() > 0) {
				lastMovementx1 = event.getX(firstPointerIndex);
				lastMovementy1 = event.getY(firstPointerIndex);
			}
			if (event.getPointerCount() > 1) {
				lastMovementx2 = event.getX(secondPointerIndex);
				lastMovementy2 = event.getY(secondPointerIndex);
				lastDistance = calcDistance();
				lastAngle = calcAngle();
			}
			break;
		}
		}
	}

	private float calcDistance() {
		float deltaX = lastMovementx2 - lastMovementx1;
		float deltaY = lastMovementy2 - lastMovementy1;
		return (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
	}

	private float calcAngle() {
		return (float) Math.atan2(lastMovementx1 - lastMovementx2,
				lastMovementy1 - lastMovementy2);
	}
}
